#ifndef __M_EXCHANGE_H__
    #define __M_EXCHANGE_H__

#include <iostream>
#include <string>
#include <unordered_map>
#include <memory>
#include <vector>
#include <mutex>
#include <sstream>
#include "../mqcommon/mq_msg.pb.h"
#include "../mqcommon/mq_loger.hpp"
#include "../mqcommon/mq_helper.hpp"

namespace mq{
    struct Exchange
    {
        using ptr = std::shared_ptr<Exchange>;//声明交换机的智能指针

        std::string name;//名称
        mq::ExchangeType type;//交换机类型
        bool durable;//持久化标志
        bool auto_delete;//自动删除标志
        google::protobuf::Map<std::string,std::string> args;//存储kv

        Exchange(){}
        Exchange(const std::string& ename,mq::ExchangeType etype,bool edurable,
                bool eauto_delete,const google::protobuf::Map<std::string,std::string>& eargs):name(ename),type(etype),durable(edurable),auto_delete(eauto_delete),args(eargs){}

        void setArgs(std::string str_args){
            //外部传递进来的格式字符串为：key=val&key=val....
            // 现在需要将key和val获取出来并加载到args中
        
            mqhelper::StrHelper splithepler;
            std::vector<std::string> res; 
            splithepler.split(str_args,"&",res);
            for(auto s : res)
            {
                //使用 find + substr 获取kv
                int pos = s.find('=',0);
                std::string key,val;
                key = s.substr(0,pos);
                val = s.substr(pos+1);

                //将 kv 插入到 map中
                // args.insert(make_pair(key,val));//使用make_pair将key插入
                args[key] = val;
            }
        }

        std::string getArgs(){
            std::string result;
            for(auto kv : args){
                result += kv.first + "=" + kv.second + "&";//直接变量args，并生成格式字符串返回出去
            } 
            return result;
        }
    };


    // 交换机的持久化管理类（联合SQLITE数据库写）
    class ExchangeMapper{
    private:
        mqhelper::SqliteHelper _sqlit_hepler;
    public:
        ExchangeMapper(const std::string& _dbfile):_sqlit_hepler(_dbfile){
            //将数据库文件给到Sqlite，此处只需要保证该文件的路径的上目录存在
            //因为数据库文件打开时，即使不存在也会创建

            //那么就先判断db_file的父级目录，然后若不存在则创建
            std::string dir = mqhelper::FileHelper::parentDirectory(_dbfile);
            if(dir != "./"){
                mqhelper::FileHelper::createDirectory(dir);
            } 
            assert(_sqlit_hepler.open());//打开数据库
            //创建表
            createTable();
        }

        //创建数据库的表：
        //结构：exchange_table(
        //  name vachar(32) primary key,
        //  type int,durable int,auto_delete int,args varchar(128) 
        //  )
        //
        void createTable(){
            #define CREATE_TABLE  "create table if not exists exchange_table(name vachar(32) primary key,type int,durable int,auto_delete int,args varchar(128));"
            //调用数据库中的exec函数执行 创建
            bool ret = _sqlit_hepler.exec(CREATE_TABLE,nullptr,nullptr);
            if(!ret){//如果失败的话就直接退出了
                DLOG("创建交换机数据库表失败！！");
                std::abort();//异常退出
            }
            DLOG("创建交换机数据库表成功");
        }

    //删除表
        void removeTable(){
            #define DROP_TABLE "drop table if exists exchange_table"
            bool ret = _sqlit_hepler.exec(DROP_TABLE,nullptr,nullptr);
            if(!ret){//如果失败的话就直接退出了
                DLOG("删除交换机数据库表失败！！");
                std::abort();//异常退出
            }
            DLOG("删除交换机数据库表成功");
        }

        bool insert(Exchange::ptr ptr){
            //ptr 存着交换机中的属性
            //也就代表着，要插入的交换机中的数据

    /*
            //c风格：
            #define INERT_SQL "insert into exchange_table values('%s',%d,%d,%d,'%s');"//'%s',%d,%d,%d,'%s' 对应着5个属性，因为SQL规定字符串要用 '' 引起来所以加上引号
            char sql[128];
            snprintf(sql,sizeof(sql),INERT_SQL,ptr->name,ptr->type,ptr->durable,ptr->auto_delete,ptr->args);//将属性值使用snprintf填充到Insert_sql中，然后赋值到sql，得到真正的插入语句
    */      
            //--------------------------------------

            //c++风格：
            std::stringstream ss;
            ss << "insert into exchange_table values(" ;
            ss << "'"<< ptr->name<<"',";
            ss << ptr->type<<"," ;
            ss << ptr->durable << "," ;
            ss << ptr->auto_delete << ", ";
            ss << "'" << ptr->getArgs()<< "');";
            
            // 直接插入允许插入失败
        bool ret = _sqlit_hepler.exec(ss.str(),nullptr,nullptr);
            if(!ret){//如果失败的话就直接退出了
                DLOG("插入交换机数据失败！！");
                return false;
            }
            DLOG("插入交换机数据成功");
            return true;
        }

        void remove(std::string name){//要删除的交换机名称
            std::stringstream ss;
            ss << "delete from exchange_table where name = \'" << name << "\';";
            bool ret = _sqlit_hepler.exec(ss.str(),nullptr,nullptr);
            if(!ret){//如果失败的话就直接退出了
                DLOG("删除 %s 数据失败！！",name.c_str());
            }
            DLOG("删除%s数据成功",name.c_str());
        }

        using ExhchangeMap = std::unordered_map<std::string,Exchange::ptr>;

        ExhchangeMap recovery(){//恢复交换机数据
            std::stringstream ss;
            ss << "select name,type,durable,auto_delete,args from exchange_table;";
            ExhchangeMap result;
            _sqlit_hepler.exec(ss.str(),selecteCallback,&result);
            return result;
        }  

    private:

        // 1. void* 是外部传递进来的参数（通过强转使用）
        // 2. int 是一行中的数据列数
        // 3. 第一个char** 是储存每一行的字符指针数组
        // 4. 第二个char** 是存储每一列的字段名称
        static int selecteCallback(void* args,int num,char** row,char** col){//注意需要静态的，否则有this指针
            ExhchangeMap* result = (ExhchangeMap*)(args);
            std::shared_ptr<Exchange> exp = std::make_shared<Exchange>();

            exp->name = row[0];
            exp->type = (mq::ExchangeType)std::stoi(row[1]);
            exp->durable = (bool)std::stoi(row[2]);
            exp->auto_delete = (bool)std::stoi(row[3]);
            exp->setArgs(row[4]);
            //括号内填的是 Exchange 构造所需要的数据
            result->insert({exp->name,exp});//插入数据{交换机名，交换机}
            return 0;//表示正常
        }
    };

    //交换机管理类（内存级）
    class ExchangeManager{
    private:
        std::mutex _mutex;
        ExchangeMapper _mapper;
        ExchangeMapper::ExhchangeMap _exchanges;//内存级交换机

    public:
        using ptr = std::shared_ptr<ExchangeManager>;//定义ExchangeManager的智能指针

        ExchangeManager(const std::string dbfile):_mapper(dbfile){
            _exchanges = _mapper.recovery();//将从文件中恢复的交换机给到内存
        }
        
        //声明/创建交换机
        bool declareExchange(const std::string& ename,mq::ExchangeType etype,  bool edurable,bool eauto_delete,const google::protobuf::Map<std::string,std::string>& eargs){
            //1. 加锁
            std::unique_lock<std::mutex> lock(_mutex);
            //判断交换机是否存在，直接使用map的find
            if(_exchanges.find(ename) != _exchanges.end()){
                //若不等则表示已存在！
                //那么就退出
                return true;
            }

            //插入到交换机
            DLOG("插入交换机:%s,type：%d",ename.c_str(),etype);
            std::shared_ptr<Exchange> exp = std::make_shared<Exchange>(ename,etype,edurable,eauto_delete,eargs);
            _exchanges.insert({ename,exp});

            if(edurable){//判断是否要持久性储存
                bool res = _mapper.insert(exp);
                if(!res){
                    return false;
                }
            }
            return true;
        }

        bool deleteExchange(const std::string& name){
            std::unique_lock<std::mutex> lock(_mutex);
            auto it = _exchanges.find(name);
            if(it == _exchanges.end()){
                //若不等则表示不存在！
                DLOG("文件不存在");
                return false;
            }
            if(it->second->durable) _mapper.remove(name);//查看是否有持久化，若有则删除底层
            _exchanges.erase(name);
            return true;
        }

        Exchange::ptr selectExchange(const std::string& name){
            std::unique_lock<std::mutex> lock(_mutex);
            auto kv = _exchanges.find(name);

            //若相等则表示不存在，返回空的交换机
            if(kv == _exchanges.end()){
                DLOG("交换机不存在！");
                return Exchange::ptr();
            }
            // DLOG("获取交换机:%s,type：%d",name.c_str,kv->second->type);

            return kv->second;//反之返回找到的交换机
        }

        bool exists(const std::string& name){
            std::unique_lock<std::mutex> lock(_mutex);
            if(_exchanges.find(name) != _exchanges.end()){
                return true;
            }
            return false;
        }

        size_t size(){
            std::unique_lock<std::mutex> lock(_mutex);
            return _exchanges.size();
        }

    //清除所有交换机
        void clear(){
            std::unique_lock<std::mutex> lock(_mutex);
            _mapper.removeTable();
            _exchanges.clear();
        }
    };
}
#endif